/*
** Copyright (C) 2009 Joshua Brent Marsh <joshua@icub3d.com>
**
** This file is part of icub3d-adt.
**  
** icub3d-adt is free software; you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation; either version 3 of the License, or
** (at your option) any later version.
**  
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
** GNU General Public License for more details.
**  
** You should have received a copy of the GNU General Public License
** along with this program; if not, write to the Free Software
** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
**  
*/

/* $Id: vector.c 35 2009-10-30 02:45:43Z joshua@icub3d.com $ */

#ifdef HAVE_CONFIG_H
#include <config.h>
#endif

#include <malloc.h>
#include <assert.h>

#include "vector.h"

void * realloc_data (void *data, size_t new_capacity);

Vector *
vector_create (size_t capacity)
{
  Vector * v;
  size_t counter;

  v = malloc (sizeof (Vector));
  if (v == NULL)
    return NULL;

  v->size = 0;
  v->max = 0;
  v->capacity = capacity;
  v->data = NULL;

  v->data = realloc_data (v->data, v->capacity);
  if (v->data == NULL)
    {
      free (v);
      return NULL;
    }

  for (counter = 0; counter < v->capacity; counter++)
    v->data[counter] = NULL;

  return v;
}

void
vector_destroy (Vector *v, destroy_func *dest_func)
{
  size_t counter;

  assert (v != NULL);

  if (dest_func != NULL)
    {
      for (counter = 0; counter < v->max + 1; counter++)
	{
	  if (v->data[counter] != NULL)
	    dest_func (v->data[counter]);
	}
    }

  free (v->data);
  free (v);
}

void *
vector_get (Vector *v, size_t pos)
{
  assert (v != NULL);

  if (pos < 0 || pos > v->max)
    return NULL;

  return v->data[pos];
}

void *
vector_remove (Vector *v, size_t pos)
{
  void *ret;

  assert (v != NULL);

  if (pos < 0 || pos > v->max)
    return NULL;

  if (v->data[pos] == NULL)
    return NULL;

  ret = v->data[pos];
  v->data[pos] = NULL;
  v->size--;
  
  /* Update the max position. */
  for (; v->max >= 0; v->max--)
    {
      if (v->data[v->max] != NULL)
	break;
    }

  return ret;
}

void *
vector_put (Vector *v, size_t pos, void *item)
{
  void **ndata;
  size_t old_capacity, counter;
  
  assert (v != NULL);

  /* If we insert past the current capacity, we need to increase the capacity to
     fit it. */
  if (pos > (v->capacity - 1))
    {
      old_capacity = v->capacity;
      ndata = realloc_data (v->data, pos + 1);
      if (ndata == NULL)
	return NULL;

      v->data = ndata;
      v->capacity = pos + 1;

      for (counter = old_capacity; counter < v->capacity; counter++)
	v->data[counter] = NULL;
    }

  if (v->data[pos] != NULL)
    return v->data[pos];

  v->data[pos] = item;
  v->size++;

  if (pos > v->max)
    v->max = pos;

  return item;
}

void *
realloc_data (void *data, size_t new_capacity)
{
  return realloc (data, sizeof (void *) * new_capacity);
}
